package com.ss.mq.pattern.rpc;

import com.rabbitmq.client.*;
import com.ss.mq.util.RabbitUtils;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;

/**
 * @author 叁昇
 * @create 2020-12-19-16:29
 */
public class RPCServer {
    public static void main(String[] args) throws Exception {
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare("rpc_queue", false, false, false, null);

        // 清除队列中的内容
        channel.queuePurge("rpc_queue");

        // 一次仅接受一条消息
        channel.basicQos(1);

        DeliverCallback deliver = (s, delivery) -> {
            int i = Integer.parseInt(new String(delivery.getBody(), StandardCharsets.UTF_8));
            BigDecimal f = fibonacci(i);
            String msg = String.valueOf(f);

            /*
                设置发回响应的 id, 与请求 id 一致, 这样客户端可以把该响应与它的请求进行对应
                correlationId：用来关联请求(request)和其调用RPC之后的回复(response)
            */
            AMQP.BasicProperties replyProps = new AMQP.BasicProperties.Builder().correlationId(delivery.getProperties().getCorrelationId()).build();

            /**
             * 发送响应消息
             * 1. 默认交换机
             * 2. 由客户端指定的,用来传递响应消息的队列名
             * 3. 参数 (关联id)
             * 4. 发回的响应消息
             */
            channel.basicPublish("", delivery.getProperties().getReplyTo(), replyProps, msg.getBytes(StandardCharsets.UTF_8));

            // 发送确认消息
            channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
        };

        CancelCallback cancel = s -> {};

        // 消费者接受消息, 等待从 rpc_queue 接受请求消息, 不自动确认
        channel.basicConsume("rpc_queue", false, deliver, cancel);
    }

    /**
     * 注意使用 BigDecimal : 第46个斐波拉契数为: 1836311903, 而 int 的最大值 2^31 = 2147483647 ( 约计 21.5 亿 )
     * 递归法在算第 47 个时已经明显缓慢, 时间复杂度是指数级增长
     * @param i 第 i 个
     * @return 返回第 i 个斐波那契数
     */
    private static BigDecimal fibonacci(int i) {
        if ( i==1 || i==2) return BigDecimal.valueOf(1);
        return fibonacci(i - 1).add(fibonacci(i - 2));
    }
}
